/*
	This script file is part of the Sonic 3 A.I.R. script software distribution.
	Copyright (C) 2017-2024 by Eukaryot

	Published under the GNU GPLv3 open source software license, see license.txt
	or https://www.gnu.org/licenses/gpl-3.0.en.html
*/



// Wind effect

//# address-hook(0x049602) end(0x049644)
function void fn049602()
{
	A2 = A0 + 0x30
	A1 = 0xffffb000
	D4.u16 = control.player1
	fn04964a()

	A2 = A0 + 0x31
	A1 = 0xffffb04a
	D4.u16 = control.tails
	fn04964a()

	D0.u16 = ((objA0.position.x.u16 + 0x0400) & 0xff80) - level.despawn_position.x
	if (D0.u16 > 0x0680)
	{
		if (objA0.respawn_entry)
		{
			A2 = 0xffff0000 + objA0.respawn_entry
			u8[A2] &= ~0x80
		}
		UnloadObject()
	}
}


//# address-hook(0x04964a) end(0x049764)
function void fn04964a()
{
	u16 dx = objA1.position.x.u16 - objA0.position.x.u16
	u16 dy = objA1.position.y.u16 - objA0.position.y.u16 + 0x0140
	if (u8[A2] == 0)
	{
		if (dx < 0x0500 && dy < 0x0280 && debug_mode.state == 0)
		{
			objA1.state = char.state.RUNNING
			u8[A1 + 0x27] = 0x01
			objA1.flags2a &= ~char.flag.CONTROL_LOCK
			objA1.flags2a |= char.flag.IN_AIR
			u8[A1 + 0x2e] = control_flag.DISABLE_UPDATE
			u8[A1 + 0x30] = 0xff		// "char.rotation.x.count"
			u8[A1 + 0x31] = 0x04		// "char.rotation.x.speed"

			u8[A1 + 0x40] = 0
			u8[A2] = 1
		}
	}
	else
	{
		if (dx < 0x0500 && debug_mode.state == 0)
		{
			objA1.velocity.x += 0x38
			D6.u16 = 0x0600
			D5.u16 = 0x18
			D0.u16 = objA1.velocity.y

			if (D4 & 0x0100)		// CONTROL_UP held
			{
				D0.u16 -= D5.u16
				D1.s16 = -D6.s16
				if (D0.s16 <= D1.s16)
				{
					D0.u16 += D5.u16
					if (D0.s16 > D1.s16)
					{
						D0.u16 = D1.u16
					}
				}
			}

			if (D4 & 0x0200)		// CONTROL_DOWN held
			{
				D0.u16 += D5.u16
				if (D0.s16 >= D6.s16)
				{
					D0.u16 -= D5.u16
					if (D0.s16 < D6.s16)
					{
						D0.u16 = D6.u16
					}
				}
			}

			objA1.velocity.y = D0.u16
			D0.u16 = objA1.velocity.y
			D1.s16 = D0.s16 >> 5
			if (D1.u16 != 0)
			{
				if (D1.s16 >= 0)
				{
					D0.u16 -= D1.u16
					if (D0.s16 < 0)
						D0.u16 = 0
					objA1.velocity.y = D0.u16
				}
				else
				{
					D0.u16 -= D1.u16
					if (D0.s16 > 0)
						D0.u16 = 0
					objA1.velocity.y = D0.u16
				}
			}

			pushAll()
			A0 = A1
			UpdateMovementStraightSimple()
			Character.UpdateRotationInAir()
			Character.CheckCollision()
			popAll()

			objA1.flags2a |= char.flag.IN_AIR
			if ((level.framecounter.low & 0x0f) == 0)
			{
				playSound(0xd4)
			}
		}
		else
		{
			u8[A1 + 0x2e] = 0
			u8[A2] = 0
		}
	}
}



// Springs and bumpers

//# address-hook(0x049766) end(0x0497ae)
function void fn049766()
{
	objA0.mapping_offset = 0x0498c2
	objA0.sprite_attributes = (sprite_attribute.PALETTE.LINE1 | 0x032d)
	objA0.render_flags |= render_flag.WORLD
	objA0.box_size.x = 0x0c
	objA0.box_size.y = 0x20
	objA0.sprite_priority = 0x0280
	D0.u8 = objA0.subtype2c
	if (D0.u8 != 0)
	{
		if (D0.s8 < 0)
		{
			objA0.update_address = 0x0497b4
			fn0497b4()
		}
		else
		{
			objA0.box_size.x = 0x08
			objA0.box_size.y = D0.u8
			objA0.update_address = 0x049804
			fn049804()
		}
	}
	else
	{
		objA0.update_address = 0x0497c2
		fn0497c2()
	}
}


//# address-hook(0x0497b4) end(0x0497bc)
function void fn0497b4()
{
	if (u8[0xfffff7c0] == 0x3f)
	{
		// All trigger buttons pressed, move the blocking bumpers away
		objA0.position.x.u16 = 0x7f00
	}
	fn0497c2()
}


//# address-hook(0x0497c2) end(0x0497fe)
function void fn0497c2()
{
	D1.u16 = 0x17
	D2.u16 = 0x20
	D3.u16 = 0x21
	D4.u16 = objA0.position.x.u16
	CheckCharacterOnClimbableObject()

	D6 = (D6 << 16) + (D6 >> 16)
	if (D6.u16 & 0x01)
	{
		A1 = 0xffffb000
		D5 = 5		// object.flag.P1_PUSHING
		fn049848()
	}

	if (D6.u16 & 0x02)
	{
		A1 = 0xffffb04a
		D5 = 6		// object.flag.P2_PUSHING
		fn049848()
	}

	DrawOrUnloadObject()
}


//# address-hook(0x049804) end(0x049842)
function void fn049804()
{
	D1.u16 = 0x13
	D2 = objA0.box_size.y
	D3.u16 = D2.u16 + 1
	D4.u16 = objA0.position.x.u16
	CheckCharacterOnClimbableObject()

	D6 = (D6 << 16) + (D6 >> 16)
	if (D6.u16 & 0x01)
	{
		A1 = 0xffffb000
		D5 = 5		// object.flag.P1_PUSHING
		fn049848()
	}

	if (D6.u16 & 0x02)
	{
		A1 = 0xffffb04a
		D5 = 6		// object.flag.P2_PUSHING
		fn049848()
	}

	CheckForUnloadObject()
}


//# address-hook(0x049848) end(0x04984a)
function void fn049848()
{
	playSound(0xaa)
	fn049850()
}


//# address-hook(0x049850) end(0x0498c0)
function void fn049850()
{
	u8 bit = (1 << D5.u8)

	objA1.flags2a &= ~char.flag.FACING_LEFT
	objA1.velocity.x = 0x0c00

	D0.u16 = objA1.position.x.u16 - objA0.position.x.u16
	if (D0.s16 < 0)
	{
		objA1.flags2a |= char.flag.FACING_LEFT
		objA1.velocity.x = -objA1.velocity.x
	}
	objA0.flags2a &= ~bit
	objA1.flags2a |= char.flag.IN_AIR
	objA1.flags2a &= ~(char.flag.CONTROL_LOCK | char.flag.PUSHING)
	u8[A1 + 0x2f] = 0
	u8[A1 + 0x40] = 0
	u16[A1 + 0x1c] = 0x01
	if (u8[A1 + 0x27] == 0)
		u8[A1 + 0x27] = 1
	objA1.state = char.state.RUNNING
	u8[A1 + 0x30] = 0xff		// "char.rotation.x.count"
	u8[A1 + 0x31] = 0x04		// "char.rotation.x.speed"
	if (u8[A1 + 0x2a] & char.flag.FACING_LEFT)
	{
		objA1.groundspeed = -objA1.groundspeed
	}
}



// Central object

//# address-hook(0x0498d2) end(0x049980)
function void fn0498d2()
{
	objA0.mapping_offset = 0x049a6c
	objA0.sprite_attributes = (sprite_attribute.PALETTE.LINE1 | 0x032d)
	objA0.render_flags |= render_flag.WORLD
	objA0.box_size.x = 0x20
	objA0.box_size.y = 0x30
	objA0.sprite_priority = 0x0280
	u16[A0 + 0x46] = objA0.position.y.u16

	// Spawn object that shows current state of the six triggers
	if (allocDynamicObjectAfterA0())
	{
	#if STANDALONE
		objA1.update_address = 0x01b58a		// Not a "real" update address, but we need something different than 0x01b588
	#else
		objA1.update_address = addressof(DrawOrUnloadObject)
	#endif
		objA1.render_flags = objA0.render_flags | render_flag.COMPOUND
		objA1.box_size.y = 0x30
		objA1.box_size.x = 0x20
		objA1.sprite_priority = 0x0200
		objA1.sprite_attributes = objA0.sprite_attributes
		objA1.mapping_offset = objA0.mapping_offset

		D2.u16 = objA0.position.x.u16
		D3.u16 = objA0.position.y.u16
		objA1.position.x.u16 = D2.u16
		objA1.position.y.u16 = D3.u16
		D4.u8 = u8[0xfffff7c0]

		A2 = A1 + 0x18
		A3 = 0x049a5a
		D1 = 6
		objA1.compound.count = D1.u16
		while (D1.s16 > 0)
		{
			u16[A2]   = D2.u16 + s8[A3]
			u16[A2+2] = D3.u16 + s8[A3+1]
			u8[A2+5]  = u8[A3+2] - ((D4.u8 & 0x01) ? 2 : 0)

			A2 += 6
			A3 += 3
			D4.u8 >>= 1
			--D1.s16
		}
		objA0.value3e = A1.u16
	}

	objA0.update_address = 0x049986
	fn049986()
}


//# address-hook(0x049986) end(0x0499fc)
function void fn049986()
{
	D0.u8 = objA0.value26
	++objA0.value26
	LookupSinCos()
	D0.s16 >>= 2
	D0.u16 += u16[A0 + 0x46]
	objA0.position.y.u16 = D0.u16

	A2 = 0xffff0000 + objA0.value3e
	A3 = A2 + 0x1a
	A4 = 0x049a5b
	D1 = 5
	while (D1.s16 >= 0)
	{
		u16[A3] = D0.u16 + s8[A4]
		A3 += 6
		A4 += 3
		--D1.s16
	}

	D1.u16 = 0x23
	D2.u16 = 0x30
	D3.u16 = 0x31
	D4.u16 = objA0.position.x.u16
	CheckCharacterOnClimbableObject()

	D6 = (D6 << 16) + (D6 >> 16)
	if (D6.u16 & 0x01)
	{
		A1 = 0xffffb000
		fn049a0e()
		D5 = 5		// object.flag.P1_PUSHING
		fn049a02()
	}

	if (D6.u16 & 0x02)
	{
		A1 = 0xffffb04a
		fn049a0e()
		D5 = 6		// object.flag.P2_PUSHING
		fn049a02()
	}

#if STANDALONE
	// Use a larger threshold to avoid object getting unloaded when bouncing back left in widescreen
	// TODO: This alone does not work, as the lights are part of a child that will disappear nevertheless
	if (!checkForUnloadObjectAtPosition(objA0.position.x.u16, 0x400))
	{
		DrawObject()
	}
#else
	DrawOrUnloadObject()
#endif
}


//# address-hook(0x049a02) end(0x049a0a)
function void fn049a02()
{
	playSound(0x74)
	fn049850()
}


//# address-hook(0x049a0e) end(0x049a58)
function void fn049a0e()
{
	D0.u16 = objA1.position.x.u16 - objA0.position.x.u16
	D1 = (D0.s16 >= 0) ? 3 : 0

	D0.u16 = objA1.position.y.u16 - objA0.position.y.u16 + 0x30
	if (D0.s16 < 0)
		D0 = 0
	if (D0.u16 >= 0x60)
		D0 = 0x40
	D0.u16 >>= 5
	D0.u16 += D1.u16
	u8[0xfffff7c0] |= (1 << D0.u8)

	A2 = 0xffff0000 + objA0.value3e
	D0.u16 = 0x1d + D0.u16 * 6
	if (u8[A2 + D0.u16] >= 3)
		u8[A2 + D0.u16] -= 2
}
